package com.bee.shepherd.common.aop;

import com.bee.shepherd.common.exception.BizRuntimeException;
import com.bee.shepherd.common.handler.JacksonHandler;
import com.bee.shepherd.web.vo.ResultData;
import com.qunar.flight.qmonitor.QMonitor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * Created by xiaolong.zhao
 * 自动识别controller来做入参,出参,以及打监控
 */
@Aspect
@Component
public class MonitorControllerAspect {

    private static final Logger LOGGER= LoggerFactory.getLogger(MonitorControllerAspect.class);

    private static final String CLASS_METHOD="_";

    //切入点
    @Pointcut("@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)")
    public void controllerAspect(){
    }

    /**
     */
    @Around("controllerAspect()")
    public Object recordControllerTrace(ProceedingJoinPoint joinPoint)throws Throwable{
        Object result=null;
        long start=System.currentTimeMillis();
        String keyWords = "http_app_";
        String methodDescription=null;
        boolean printInput=true;
        boolean printOutput=true;
        try {
            Signature signature = joinPoint.getSignature();
            Class declaringType = signature.getDeclaringType();
            //
            methodDescription=declaringType.getSimpleName()+CLASS_METHOD+signature.getName();
            keyWords=keyWords+signature.getName();//监控名
            LOGGER.info("signature.getName:{}", signature.getName());
            MethodSignature targetMethod=null;
            if (signature instanceof MethodSignature){
                targetMethod=(MethodSignature)signature;
            }
            //
            if (targetMethod!=null){
                Class[] parameterTypes = targetMethod.getParameterTypes();
                Method realMethod = ReflectionUtils.findMethod(declaringType, signature.getName(), parameterTypes);
                MonitorIgnore monitorIgnore = AnnotationUtils.findAnnotation(realMethod, MonitorIgnore.class);
                if (monitorIgnore != null) {
                    printInput = monitorIgnore.printInput();
                    printOutput = monitorIgnore.printOutput();
                }
                //
                if (filterNotAllowPrintIn(parameterTypes) && printInput) {
                    LOGGER.info("{} recordControllerTrace request:{}", methodDescription, JacksonHandler.toSerialize(joinPoint.getArgs()));
                }

                //再次check下
                if (printOutput){
                    printOutput=filterNotAllowPrintOut(realMethod.getReturnType());
                }
            }
            result = joinPoint.proceed();
        }catch (Throwable e){
            LOGGER.error("{} recordControllerTrace error",methodDescription,e);
            if (e instanceof BizRuntimeException) {
                LOGGER.error("{} biz error", methodDescription);
                QMonitor.recordOne(keyWords + "_biz_error");
                QMonitor.recordOne("total_biz_error");
                result = ResultData.buildBusinessFailure(e.getMessage());
            }else if (e instanceof RuntimeException) {
                LOGGER.error("{} biz other error", methodDescription);
                QMonitor.recordOne(keyWords + "_biz_other_error");
                QMonitor.recordOne("total_biz_other_error");
                result = ResultData.buildBusinessFailure(e.getMessage());
            }else {
                LOGGER.error("{} sys error", methodDescription);
                QMonitor.recordOne(keyWords + "_sys_error");
                QMonitor.recordOne("total_sys_error");
                result = ResultData.builderFailure();
            }
        }finally {
            long duration = System.currentTimeMillis() - start;
            QMonitor.recordOne(keyWords+"_total", duration);
            QMonitor.recordOne("whole_request_total",duration);
            LOGGER.info("{} recordControllerTrace totalTime:{}",methodDescription,duration);
            if (printOutput){
                LOGGER.info("{} recordControllerTrace response:{}", methodDescription,JacksonHandler.toSerialize(result));
            }
        }
        return result;
    }

    private boolean filterNotAllowPrintIn(Class[] parameterTypes){
        if (parameterTypes==null || parameterTypes.length==0){
            return true;
        }
        //
        for (Class temClass:parameterTypes){
            if (HttpServletRequest.class.isAssignableFrom(temClass)){
                return false;
            }
            if (HttpServletResponse.class.isAssignableFrom(temClass)){
                return false;
            }
        }
        return true;
    }

    private boolean filterNotAllowPrintOut(Class<?> retClass){
        if (retClass.isInstance(ModelAndView.class)){
            return false;
        }
        //
        return true;
    }
}
